// 1. call
// 2. apply
// 3. bind
// 4. curry
// 5. compose
// 6. debounce
// 7. throttle

// 8. lodash.chunk
// 9. lodash.get
// 10. lodash.memorize
// 11. lodash.countBy


Function.prototype.myCall = function (context = window, ...args) {
    // 基本思路就是在context 上挂一个属性，属性指向this 执行后，删除此属性
    if (Function.prototype instanceof this) {
        return new Error('不能是自己')
    }

    let x = Symbol();
    context[x] = this;
    let res = context[x](...args) //  一个个参数
    delete context[x]
    return res;
}

Function.prototype.myApply = function (context = window, ...args) {
    // 基本思路就是在context 上挂一个属性，属性指向this 执行后，删除此属性
    if (Function.prototype instanceof this) {
        return new Error('不能是自己')
    }

    let x = Symbol();
    context[x] = this;
    let res;
    if (Array.isArray(args)) {
        res = context[x](args) // 数组
    } else {
        res = context[x]()
    }
    delete context[x]
    return res;
}


Function.prototype.myBind = function (context = window, ...args) {
    // 基本思路就是在context 上挂一个属性，属性指向this 执行后，删除此属性
    if (Function.prototype instanceof this) {
        return new Error('不能是自己')
    }

    return function (...args) {
        let x = Symbol();
        context[x] = this;
        let res = context[x](...args) // 数组
        delete context[x]
        return res;
    }
}

// 分为两种版本1，确定参数； 2.不确定参数
// https://blog.csdn.net/wingxabc/article/details/111167582
function curry(fn) {
    return function curryFn(...args) {
        if (args.length >= fn.length) {
            return fn.call(this, ...args)
        } else {
            return function (...resetArgs) {
                return curryFn.call(this, ...args.concat(resetArgs))
            }
        }
    }
}
var a = (a, b, c, d, e) => a + b + c + d + e
let newFn = curry(a)
console.log(newFn(1, 2)(3, 4, 5, 6, 6))

// 5. sum(1)(2)(3)
function curry2(...args){
    const parmas = args;
    function sum(){
        parmas = [...args, ...arguments];
        return sum
    }
    sum.toString = function(){
        return parmas.reduce((pre, next) => (pre + next), 0)
    }
    return sum
}
console.log(curry2(1)(2)(3).toString())


function compose(...args) {
    return function (val) {
        return args.reduceRight((prev, next) => {
            return next(prev)
        }, val)
    }
}
let a1 = (a) => a + a;
let a2 = (a) => a * a;
console.log(compose(a1, a2)(10))


function debounce(fn, wait) {
    let timer = null;
    return function (...args) {
        clearTimeout(timer)
        timer = setTimeout(() => {
            fn.call(this, ...args)
        }, wait)
    }
}
// 第一次就执行
function debounceBetter(fn, wait, immediate) {
    let timer = null;
    return function (...args) {
        if (timer) clearTimeout(timer)
        if (immediate && !timer) {
            timer = setTimeout(() => {
                timer = null;
            })
            fn.call(this, ...args)
        } else {
            timer = setTimeout(() => {
                fn.call(this, ...args)
                timer = null
            }, wait)
        }
    }
}

function throttle(fn, wait) {
    let timer = null;
    return function (...args) {
        if (!timer) {
            timer = setTimeout(() => {
                fn.call(this, ...args)
                timer = null
            }, wait)
        }
    }
}

// 更准确的版本
function throttleBetter(fn, wait) {
    let timer = null;
    let starttime = Date.now()
    return function (...args) {
        let currtime = Date.now();
        let time = wait - (currtime - starttime);
        if (time <= 0) {
            if (timer) {
                clearTimeout(timer)
                timer = null
            }
            fn.call(this, ...args)
            starttime = Date.now()
        } else {
            if (!timer) {
                timer = setTimeout(() => {
                    fn.call(this, ...args)
                    timer = null
                    starttime = Date.now()
                }, time)
            }
        }
    }
}

// lodash.get
// lodash.chunk
// lodash.countBy
// lodash.memorize

function chunk(arr, size){
    if(size < 1) return [];
    const result = [];
    for(let i = 0; i< arr.length; i+= size){
        result.push(arr.slice(i, i + size));
    }
    return result
}
console.log(chunk(['a', 'b', 'c','d']))

function get(object, path, defaultValue){
    let obj = object;
    if(typeof path === 'string'){
        const reg = /[^\[\].]+/g;
        path = path.match(reg)
    }

    for(const key of path){
        if(!obj) return defaultValue;
        obj = obj[key]
    }
    return obj === undefined ? defaultValue : obj
}
const obj = {a:[{b: {c: 3}}]}
console.log(get(obj, ['k','0','b', 'c']))
console.log(get(obj, "k[0].b.c", 3))

function memoize(func, resolver){
    const memoized = function(...args){
        const key = resolver ? resolver.apply(this, args) : args[0]
        if(memoized.cache.has(key)){
            return memoized.cache.get(key)
        }
        const result = func.apply(this, args)
        memoized.cache.set(result);
        return result
    }
    memoized.cache = new WeakMap();
    return memoized;
}

var object = {a: 1, b: 2}
var other = {c: 3, d: 4}

var values = memoize((object) => Object.values(object))
// 每次返回新对象，都要重新算没有缓存
var values2 = memoize((object) => Object.values(object), () => ({}))
console.log(values(object))
object.a = 2;
console.log(values(object))


function countBy(collection, iteratee) {
    const result = {};
    for(const item of collection){
        const key = iteratee(item)
        result[key] = result[key] ? result[key] + 1 : 1
    }
    return result;
}
const user = [
    {user: 1, active: true},
    {user: 2, active: true},
    {user: 3, active: false}
]
console.log(countBy(user, (value) =>  value.active))